﻿/// <summary>
/// Developer: CHOOI KAH WAI
/// Last Modified: 26/10/2009
/// </summary>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Timers;
using ARSS.Visual;
using ARSS.Client;
using System.IO;
using System.Windows.Threading;

namespace ARSS.Common
{
    /// <summary>
    /// Action marker information for visual class usage
    /// </summary>
    public class ActionMarker
    {
        #region Variables

        private SceneNode lockedNode;

        private DispatcherTimer countdownTimer;


        #endregion

        #region Properties

        /// <summary>
        /// Is this marker on screen?
        /// </summary>
        public bool IsDetected { get; set; }

        /// <summary>
        /// Is this marker on use?
        /// </summary>
        public bool IsUsing { get; set; }

        /// <summary>
        /// AR code for this marker
        /// </summary>
        public int ARCode { get; set; }

        /// <summary>
        /// Marker scene node
        /// </summary>
        public SceneNode SNode { get; set; }
        

        /// <summary>
        /// Count-down counter
        /// </summary>
        public int Counter { get; set; }

        /// <summary>
        /// Locked node
        /// </summary>
        public SceneNode LockedNode{
            get
            {
                return this.lockedNode;
            }
            set
            {
                this.lockedNode = value; 
            }
        }

        /// <summary>
        /// Ignore this marker?
        /// </summary>
        public bool Ignore { get; set; }


        #endregion

        #region Functions
        
        /// <summary>
        /// Constructor
        /// </summary>
        public ActionMarker()
        {
            Counter = 0;
            IsDetected = false;
            IsUsing = false;
            ARCode = -1;
            SNode = null;
            this.lockedNode = null;
            this.countdownTimer = new DispatcherTimer();
            this.countdownTimer.Interval = TimeSpan.FromSeconds(1);
            this.countdownTimer.Tick += new EventHandler(countdownTimer_Tick);

            Ignore = false;
        }

        /// <summary>
        /// Countdown timer ticks
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void countdownTimer_Tick(object sender, EventArgs e)
        {
            Counter++;

            SceneNode sn = Core.Instance.GetCollision(Util.MatrixUtil.GetMatrixFromWPF(SNode.Model3D.Transform.Value));

            // Quit processing if no object collides with the marker
            if (sn == null) return;

            // Can only lock free object
            if (sn.Locker.Length > 0) return;

            // Different node?
            if (sn != this.lockedNode)
            {
                //reset counter
                Counter = 0;
                LockedNode = sn;
                if (this.lockedNode != null)
                    Core.Instance.DisplayMsg("Locking " + this.lockedNode.Name);
                else
                    Core.Instance.DisplayMsg("");

            }

            // 3 seconds has passed
            if (Counter >= 3 && sn != null)
            {
                Core.Instance.DisplayMsg("Locked " + this.lockedNode.Name);
                this.countdownTimer.Stop();

                IsUsing = true;
                // Scene node locked down

                // Extract the scene node's transformation
                this.lockedNode.ParentNode.Children.Remove(this.lockedNode);
                SNode.Children.Add(this.lockedNode);
                this.lockedNode.ParentNode = SNode;
                Matrix3D matLN = LockedNode.Model3D.Transform.Value;
                Matrix3D matSN = SNode.Model3D.Transform.Value;
                matSN.Invert();
                matLN = Matrix3D.Multiply(matLN, matSN);
                // Discard the position information
                matLN.OffsetX = 0;
                matLN.OffsetY = 0;
                matLN.OffsetZ = 0;

                this.lockedNode.TransformMatrix = matLN;


                // Notify Host
                MemoryStream ms = new MemoryStream();
                // Action Code
                ms.Write(BitConverter.GetBytes((int)NetworkActionCodesEnum.LockNode), 0, 4);
                byte[] nodeNameBuffer = System.Text.ASCIIEncoding.UTF8.GetBytes(this.lockedNode.Name);
                // Node Name Length
                ms.Write(BitConverter.GetBytes(nodeNameBuffer.Length), 0, 4);
                // Node Name buffer
                ms.Write(nodeNameBuffer, 0, nodeNameBuffer.Length);
                // Nickname length
                byte[] nicknameBuffer = System.Text.ASCIIEncoding.UTF8.GetBytes(Core.Instance.MyData.Name);
                ms.Write(BitConverter.GetBytes(nicknameBuffer.Length), 0, 4);
                // Nickname data
                ms.Write(nicknameBuffer, 0, nicknameBuffer.Length);

                Core.Instance.MyData.SendData(ms.GetBuffer(), (int)ms.Length);


                Logger.Instance.Log(this.lockedNode.Name + " locked by " + SNode.Name);
            }

        }

        /// <summary>
        /// Start the count down timer
        /// </summary>
        public void startCountDown()
        {
            this.countdownTimer.Start();
        }

        /// <summary>
        /// Count down timer elapsed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void countdown_Elapsed(object sender, ElapsedEventArgs e)
        {
            
        }

        /// <summary>
        /// Marker released
        /// </summary>
        public void release()
        {
            LockedNode.Locker = "";

            // Notify Host
            MemoryStream ms = new MemoryStream();
            // Action Code
            ms.Write(BitConverter.GetBytes((int)NetworkActionCodesEnum.ReleaseNode), 0, 4);
            byte[] nodeNameBuffer = System.Text.ASCIIEncoding.UTF8.GetBytes(this.lockedNode.Name);
            // Node Name Length
            ms.Write(BitConverter.GetBytes(nodeNameBuffer.Length), 0, 4);
            // Node Name buffer
            ms.Write(nodeNameBuffer, 0, nodeNameBuffer.Length);

            Core.Instance.MyData.SendData(ms.GetBuffer(), (int)ms.Length);


            LockedNode = null;

            Core.Instance.DisplayMsg("");
            this.countdownTimer.Stop();
        }

        #endregion
    }
}
